/* Copyright 2009-2015 Free Software Foundation, Inc.

   This file is part of the Xilinx MicroBlaze simulator.

   This library is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */

/*
 *  MICROBLAZE Instruction Set Architecture
 *
 *  INSTRUCTION(NAME,
 *              OPCODE,
 *              TYPE,
 *              SEMANTICS)
 *
 */

INSTRUCTION(add,
	    0x00,
	    INST_TYPE_RD_RA_RB,
	    CARRY = C_calc(RA, RB, 0);
	    RD = RA + RB;
	    C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(rsub,
	    0x01,
            INST_TYPE_RD_RA_RB, 
	    CARRY = C_calc(RB, ~RA, 1);
	    RD = RB + ~RA + 1;
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(addc,
	    0x02,
            INST_TYPE_RD_RA_RB,
	    CARRY = C_calc(RA, RB, C_rd);
	    RD = RA + RB + C_rd;
	    C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(rsubc,
	    0x03,
            INST_TYPE_RD_RA_RB,
	    CARRY = C_calc(RB, ~RA, C_rd);
	    RD = RB + ~RA + C_rd;
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(addk,
	    0x04,
            INST_TYPE_RD_RA_RB,
	    RD = RA + RB;
	    PC += INST_SIZE)

INSTRUCTION(rsubk,
	    0x05,
            INST_TYPE_RD_RA_RB,
	    RD = RB + ~RA + 1;
	    PC += INST_SIZE)

INSTRUCTION(cmp,
            0x05,
            INST_TYPE_RD_RA_RB,
            {
	      int tmp_reg = RB + ~RA + 1;
	      if ((RB & 0x80000000) ^ (RA & 0x80000000)) {
		tmp_reg = ((tmp_reg & 0x7fffffff) | (RB & 0x80000000));
	      }
	      RD = tmp_reg;
	      PC += INST_SIZE;
	    })

INSTRUCTION(cmpu,
            0x05,
            INST_TYPE_RD_RA_RB,
            {
	      int tmp_reg = RB + ~RA + 1;
	      if ((RB & 0x80000000) ^ (RA & 0x80000000)) {
		tmp_reg = ((tmp_reg & 0x7fffffff) | (RA & 0x80000000));
	      }
	      RD = tmp_reg;
	      PC += INST_SIZE;
	    })

INSTRUCTION(addkc,
	    0x06,
            INST_TYPE_RD_RA_RB,
	    RD = RA + RB + C_rd;
	    PC += INST_SIZE)

INSTRUCTION(rsubkc,
	    0x07,
            INST_TYPE_RD_RA_RB,
	    RD = RB + ~RA + C_rd;
	    PC += INST_SIZE)

INSTRUCTION(addi,
	    0x08,
	    INST_TYPE_RD_RA_IMM,
            CARRY = C_calc(RA, IMM, 0);
	    RD = RA + IMM;
	    C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(rsubi,
	    0x09,
	    INST_TYPE_RD_RA_IMM,
            CARRY = C_calc(IMM, ~RA, 1);
	    RD = IMM + ~RA + 1;
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(addic,
	    0x0A,
            INST_TYPE_RD_RA_IMM,
            CARRY = C_calc(RA, IMM, C_rd);
	    RD = RA + IMM + C_rd;
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(rsubic,
	    0x0B,
            INST_TYPE_RD_RA_IMM,
            CARRY = C_calc(IMM, ~RA, C_rd);
	    RD = IMM + ~RA + C_rd;
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(addik,
	    0x0C,
            INST_TYPE_RD_RA_IMM,
	    RD = RA + IMM;
	    PC += INST_SIZE)

INSTRUCTION(rsubik,
	    0x0D,
            INST_TYPE_RD_RA_IMM,
	    RD = IMM + ~RA + 1;
	    PC += INST_SIZE)

INSTRUCTION(addikc,
	    0x0E,
            INST_TYPE_RD_RA_IMM,
	    RD = RA + IMM + C_rd;
	    PC += INST_SIZE)

INSTRUCTION(rsubikc,
	    0x0F,
            INST_TYPE_RD_RA_IMM,
            RD = IMM + ~RA + C_rd;
	    PC += INST_SIZE)

INSTRUCTION(mul,
	    0x10,
            INST_TYPE_RD_RA_RB, 
	    RD = RA * RB;
	    PC += INST_SIZE)

INSTRUCTION(bsrl,
	    0x11,
	    INST_TYPE_RD_RA_RB,
	    RD = (uword)RA >> RB;
	    PC += INST_SIZE)

INSTRUCTION(bsra,
	    0x11,
	    INST_TYPE_RD_RA_RB,
	    RD = (word)RA >> RB;
	    PC += INST_SIZE)

INSTRUCTION(bsll,
	    0x11,
	    INST_TYPE_RD_RA_RB,
	    RD = (uword)RA << RB;
	    PC += INST_SIZE)

INSTRUCTION(idiv,
	    0x12,
	    INST_TYPE_RD_RA_RB,
	    RD = (word) RB / (word) RA;
	    PC += INST_SIZE)

INSTRUCTION(idivu,
	    0x12,
	    INST_TYPE_RD_RA_RB,
	    RD = (uword) RB / (uword) RA;
	    PC += INST_SIZE)

INSTRUCTION(muli,
	    0x18,
            INST_TYPE_RD_RA_IMM,
	    RD = RA * IMM;
	    PC += INST_SIZE)

INSTRUCTION(bsrli,
	    0x19,
	    INST_TYPE_RD_RA_IMM5,
	    RD = (uword)RA >> (IMM & 0x1F);
	    PC += INST_SIZE)

INSTRUCTION(bsrai,
	    0x19,
	    INST_TYPE_RD_RA_IMM5,
	    RD = (word)RA >> (IMM & 0x1F);
	    PC += INST_SIZE)

INSTRUCTION(bslli,
	    0x19,
	    INST_TYPE_RD_RA_IMM5,
	    RD = (uword)RA << (IMM & 0x1F);
	    PC += INST_SIZE)

INSTRUCTION(get,
            0x1b,
            INST_TYPE_RD_IMM12,
            PC += INST_SIZE)

INSTRUCTION(put,
            0x1b,
            INST_TYPE_R1_IMM12,
            PC += INST_SIZE)

INSTRUCTION(nget,
            0x1b,
            INST_TYPE_RD_IMM12,
            PC += INST_SIZE)

INSTRUCTION(nput,
            0x1b,
            INST_TYPE_R1_IMM12,
            PC += INST_SIZE)

INSTRUCTION(cget,
            0x1b,
            INST_TYPE_RD_IMM12,
            PC += INST_SIZE)

INSTRUCTION(cput,
            0x1b,
            INST_TYPE_R1_IMM12,
            PC += INST_SIZE)

INSTRUCTION(ncget,
            0x1b,
            INST_TYPE_RD_IMM12,
            PC += INST_SIZE)

INSTRUCTION(ncput,
            0x1b,
            INST_TYPE_R1_IMM12,
            PC += INST_SIZE) 

INSTRUCTION(microblaze_or,
	    0x20,
            INST_TYPE_RD_RA_RB,
	    RD = RA | RB;
	    PC += INST_SIZE)

INSTRUCTION(microblaze_and,
	    0x21,
            INST_TYPE_RD_RA_RB,
	    RD = RA & RB;
	    PC += INST_SIZE)

INSTRUCTION(microblaze_xor,
	    0x22,
            INST_TYPE_RD_RA_RB,
	    RD = RA ^ RB;
	    PC += INST_SIZE)

INSTRUCTION(andn,
	    0x23,
            INST_TYPE_RD_RA_RB,
	    RD = RA & ~RB;
	    PC += INST_SIZE)

INSTRUCTION(sra,
	    0x24,
	    INST_TYPE_RD_RA,
	    CARRY = (RA & 0x1);
	    RD = (int) (RA >> 1);
	    C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(src,
	    0x24,
            INST_TYPE_RD_RA,
	    CARRY = (RA & 0x1);
            RD = ((((int) (RA >> 1)) & 0x7FFFFFFF) | (uword)(C_rd << 31));
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(srl,
	    0x24,
            INST_TYPE_RD_RA,
            CARRY = (RA & 0x1);
	    RD = (uword) ((RA >> 1) & 0x7FFFFFFF);
            C_wr(CARRY);
	    PC += INST_SIZE)

INSTRUCTION(sext8,
	    0x24,
            INST_TYPE_RD_RA,
	    RD = MICROBLAZE_SEXT8(RA);
	    PC += INST_SIZE)

INSTRUCTION(sext16,
            0x24,
            INST_TYPE_RD_RA,
	    RD = MICROBLAZE_SEXT16(RA);
            PC += INST_SIZE)

INSTRUCTION(wdc,
            0x24,
            INST_TYPE_RA_RB,
            PC += INST_SIZE)

INSTRUCTION(wic,
            0x24,
            INST_TYPE_RA_RB,
            PC += INST_SIZE)

INSTRUCTION(mts,
	    0x25,
	    INST_TYPE_SA_RA,
	    SA = RA;
	    PC += INST_SIZE)

INSTRUCTION(mfs,
	    0x25,
            INST_TYPE_RD_SA,
	    RD = SA;
	    PC += INST_SIZE)

INSTRUCTION(br,
	    0x26,
	    INST_TYPE_RB,
	    PC += RB;
	    BRANCH)

INSTRUCTION(brd,
            0x26,
            INST_TYPE_RB,
            PC += RB;
	    BRANCH;
            DELAY_SLOT)

INSTRUCTION(brld,
	    0x26,
	    INST_TYPE_RD_RB,
	    RD = PC;
	    PC += RB;
	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(bra,
	    0x26,
            INST_TYPE_RB,
	    PC = RB;
	    BRANCH)

INSTRUCTION(brad,
            0x26,
            INST_TYPE_RB,
            PC = RB;
	    BRANCH;
            DELAY_SLOT)

INSTRUCTION(brald,
	    0x26,
            INST_TYPE_RD_RB,
	    RD = PC;
	    PC = RB;
	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(microblaze_brk,
	    0x26,
            INST_TYPE_RD_RB,
	    RD = PC;
	    PC = RB;
            MSR = MSR | BIP_MASK;
	    BRANCH)

INSTRUCTION(beq,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA == 0) {
	      PC += RB;
	      BRANCH;
            } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(beqd,
            0x27,
            INST_TYPE_RA_RB,
            if (RA == 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bne,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA != 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bned,
            0x27,
            INST_TYPE_RA_RB,
            if (RA != 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(blt,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA < 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bltd,
            0x27,
            INST_TYPE_RA_RB,
            if (RA < 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(ble,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA <= 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bled,
            0x27,
            INST_TYPE_RA_RB,
            if (RA <= 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bgt,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA > 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bgtd,
            0x27,
            INST_TYPE_RA_RB,
            if (RA > 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bge,
	    0x27,
            INST_TYPE_RA_RB,
	    if (RA >= 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bged,
            0x27,
            INST_TYPE_RA_RB,
            if (RA >= 0) {
	      PC += RB;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(ori,
	    0x28,
            INST_TYPE_RD_RA_IMM,
	    RD = RA | IMM;
	    PC += INST_SIZE)

INSTRUCTION(andi,
	    0x29,
            INST_TYPE_RD_RA_IMM,
	    RD = RA & IMM;
	    PC += INST_SIZE)

INSTRUCTION(xori,
	    0x2A,
            INST_TYPE_RD_RA_IMM,
	    RD = RA ^ IMM;
	    PC += INST_SIZE)

INSTRUCTION(andni,
	    0x2B,
            INST_TYPE_RD_RA_IMM,
	    RD = RA & ~IMM;
	    PC += INST_SIZE)

INSTRUCTION(imm,
	    0x2C,
	    INST_TYPE_IMM,
	    IMM_H = IMM_L;
	    PC += INST_SIZE)

INSTRUCTION(rtsd,
	    0x2D,
            INST_TYPE_RA_IMM,
	    PC = RA + IMM;
    	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(rtid,
	    0x2D,
            INST_TYPE_RA_IMM,
	    PC = RA + IMM;
	    MSR = MSR | INTR_EN_MASK;
	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(rtbd,
	    0x2D,
	    INST_TYPE_RA_IMM,
	    PC = RA + IMM;
	    MSR = MSR & ~BIP_MASK;
	    BRANCH;
            DELAY_SLOT;)

INSTRUCTION(bri,
	    0x2E,
            INST_TYPE_IMM,
	    PC += IMM;
	    BRANCH)

INSTRUCTION(brid,
            0x2E,
            INST_TYPE_IMM,
            PC += IMM;
	    BRANCH;
            DELAY_SLOT)

INSTRUCTION(brlid,
	    0x2E,
            INST_TYPE_RD_IMM,
	    RD = PC;
	    PC += IMM;
	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(brai,
	    0x2E,
            INST_TYPE_IMM,
	    PC = IMM;
	    BRANCH)

INSTRUCTION(braid,
            0x2E,
            INST_TYPE_IMM,
            PC = IMM;
	    BRANCH;
            DELAY_SLOT)

INSTRUCTION(bralid,
	    0x2E,
	    INST_TYPE_RD_IMM,
	    RD = PC;
	    PC = IMM;
	    BRANCH;
	    DELAY_SLOT)

INSTRUCTION(brki,
	    0x2E,
            INST_TYPE_RD_IMM,
	    RD = PC;
	    PC = IMM;
            MSR = MSR | BIP_MASK;
	    BRANCH)

INSTRUCTION(beqi,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA == 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(beqid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA == 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bnei,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA != 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bneid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA != 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(blti,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA < 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bltid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA < 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(blei,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA <= 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bleid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA <= 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bgti,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA > 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bgtid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA > 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(bgei,
	    0x2F,
            INST_TYPE_RA_IMM,
	    if (RA >= 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    })

INSTRUCTION(bgeid,
            0x2F,
            INST_TYPE_RA_IMM,
            if (RA >= 0) {
	      PC += IMM;
	      BRANCH;
	    } else {
	      PC += INST_SIZE;
	    }
	    DELAY_SLOT)

INSTRUCTION(lbu,
	    0x30,
            INST_TYPE_RD_RA_RB,
	    RD = (MEM_RD_UBYTE(RA + RB));
	    PC += INST_SIZE)

INSTRUCTION(lhu,
	    0x31,
	    INST_TYPE_RD_RA_RB,
	    RD = (MEM_RD_UHALF((RA + RB) & ~0x1));
	    PC += INST_SIZE)

INSTRUCTION(lw,
	    0x32,
            INST_TYPE_RD_RA_RB, 
	    RD = (MEM_RD_WORD((RA + RB) & ~0x3));
	    PC += INST_SIZE)

INSTRUCTION(sb,
	    0x34,
            INST_TYPE_RD_RA_RB,
	    MEM_WR_BYTE(RA + RB, RD);
	    PC += INST_SIZE)

INSTRUCTION(sh,
	    0x35,
            INST_TYPE_RD_RA_RB,
	    MEM_WR_HALF((RA + RB) & ~0x1, RD);
	    PC += INST_SIZE)

INSTRUCTION(sw,
	    0x36,
            INST_TYPE_RD_RA_RB,
	    MEM_WR_WORD((RA + RB) & ~0x3, RD);
	    PC += INST_SIZE)

INSTRUCTION(lbui,
	    0x38,
            INST_TYPE_RD_RA_IMM,
	    RD = (MEM_RD_UBYTE(RA + IMM));
	    PC += INST_SIZE)

INSTRUCTION(lhui,
	    0x39,
            INST_TYPE_RD_RA_IMM,
	    RD = (MEM_RD_UHALF((RA+IMM) & ~0x1));
	    PC += INST_SIZE)

INSTRUCTION(lwi,
	    0x3A,
            INST_TYPE_RD_RA_IMM, 
	    RD = (MEM_RD_WORD((RA+IMM) & ~0x3));
	    PC += INST_SIZE)

INSTRUCTION(sbi,
	    0x3C,
            INST_TYPE_RD_RA_IMM,
	    MEM_WR_BYTE(RA + IMM, RD);
	    PC += INST_SIZE)

INSTRUCTION(shi,
	    0x3D,
            INST_TYPE_RD_RA_IMM,
	    MEM_WR_HALF((RA + IMM) & ~0x1, RD);
	    PC += INST_SIZE)

INSTRUCTION(swi,
	    0x3E,
            INST_TYPE_RD_RA_IMM,
	    MEM_WR_WORD((RA + IMM) & ~0x3, RD);
	    PC += INST_SIZE)
